package com.gosophia.metadataEngine.commons;


import java.io.ByteArrayOutputStream;

import java.io.IOException;
import java.util.List;

import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.util.Assert;

import com.gosophia.metadataEngine.dao.MetaDataFiledDao;
import com.gosophia.metadataEngine.dao.MetaDataTableDao;
import com.gosophia.metadataEngine.entity.DataSourceInfo;
import com.gosophia.metadataEngine.entity.MetaDataField;
import com.gosophia.metadataEngine.entity.MetaDataTable;

/**
 * hibernate congfigure文件生成工具
 * @author mayue
 *
 */
public class CreateXML {
    private static final String[] DOC_TYPE = { "hibernate-mapping",
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN",
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" };
    
    private static final String[] DOC_DATASOURCE = { "hibernate-configuration",
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN",
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd" };

    private static final String ROOT_NODE = "hibernate-mapping";
    
    private static final String CLASS_NODE = "class";
    
    private static final String ENTITY_NAME_ATTRIBUTE = "entity-name";
    
    private static final String TABLE_ATTRIBUTE = "table";
    
    private static final String ID_NODE = "id";
    
    private static final String NAME_ATTRIBUTE = "name";
    
    private static final String NAME_ATTRIBUTE_VALUE = "id";
    
    private static final String TYPE_ATTRIBUTE = "type";
    
    
    private static final String TYPE_ATTRIBUTE_VALUE = "long";
    
    private static final String GENERATOR_NODE = "generator";
    
    private static final String GENERATOR_CLASS_ATTRIBUTE = "class";
    //改成native
    private static final String GENERATOR_CLASS_ATTRIBUTE_TYPE = "native";
    
    private static final String PROPERTY_NODE = "property";
    
    private static final String MANY_TO_ONE = "many-to-one";
    
    private static final String ENTITY_NAME = "entity-name";
    
    private static final String PROPERTY_REF = "property-ref";
    
    private static final String COLUMN = "column";
    
    
    //以下为创建数据源配置文件所需常量
    private static final String CONFIGURATION_NODE = "hibernate-configuration";
    
    private static final String SESSION_NODE = "session-factory";
    
    private static final String SHOW_SQL = "show_sql";
    
    private static final String FORMAT_SQL= "format_sql";
    
    private static final String HIBERNATE_DIALECT = "hibernate.dialect";
    
    private static final String HIBERNATE_CONNECTION_DRIVER_CLASS = "hibernate.connection.driver_class";
    
    private static final String HIBERNATE_CONNECTION_URL = "hibernate.connection.url";
    
    private static final String HIBERNATE_CONNECTION_USERNAME = "hibernate.connection.username";
    
    private static final String HIBERNATE_CONNECTION_PASSWORD = "hibernate.connection.password";
    
    private static final String CURRENT_SESSION_CONTEXT_CLASS = "hibernate.current_session_context_class";
    
    
    private static final String OUT_FK = "OUT_FK";//引用外部数据字典标志
    
    private static final String IN_FK = "IN_FK";//引用内部数据字典标志
    
    
    
    /**
     * 将doc文档转化为String
     * @param doc
     * @return
     */
    public static String changeDocToStr(Document doc) {

        Assert.notNull(doc);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try {
            /** 将doc中的内容写入文件中 */
            OutputFormat format = OutputFormat.createPrettyPrint();

            // FileWriter fileWriter = new FileWriter(filePath);
            outputStream = new ByteArrayOutputStream();

            XMLWriter writer = new XMLWriter(outputStream, format);

            writer.write(doc);

            writer.close();

        } catch (IOException ex) {

            // LOGGER.error(ex.getMessage());
            ex.printStackTrace();

        }
        return outputStream.toString();
    }
    /**
     * 创建实体hbm.xml文档
     * @param tableId：指定系统在元数据表中的id
     * @param metaDataTableDao：元数据表dao
     * @param metaDataFiledDao：元数据字段dao
     * @return
     */
    public  static Document getDocument(Long tableId,MetaDataTableDao metaDataTableDao,MetaDataFiledDao metaDataFiledDao) {
        
        MetaDataTable metaDataTable = metaDataTableDao.get(tableId);
        List<MetaDataField> metaDataFields = metaDataFiledDao.getByProperty("metaDataTable.metaDataTableId", tableId);
        
        Assert.notNull(metaDataTable,"metaDataTable不能为null");
        Assert.notNull(metaDataFields,"metaDataFields不能为null");
        
        /** 建立document对象 */
        Document document = DocumentHelper.createDocument();
        /**
         * 定义文档类型
         */
        document.addDocType(DOC_TYPE[0], DOC_TYPE[1], DOC_TYPE[2]);
        /** 建立XML文档的根节点hibernate-mapping */
        Element hibernate_mapping = document.addElement(ROOT_NODE);
        /** 添加class节点*/
        Element classNode = hibernate_mapping.addElement(CLASS_NODE);
        /** 为class节点添加属性参数*/
        classNode.addAttribute(ENTITY_NAME_ATTRIBUTE, metaDataTable
                .getEntityClassName());
        classNode.addAttribute(TABLE_ATTRIBUTE, metaDataTable.getTableName());
        for(MetaDataField mdf : metaDataFields){
            if(mdf.getIsPk()){
                /**判断是否是主键，如果是则创建id节点，并添加相关参数*/
                Element idNode = classNode.addElement(ID_NODE);
                /**为id节点添加name属性*/
                idNode.addAttribute(NAME_ATTRIBUTE,mdf.getPrivateName());
                /**为id节点添加type属性*/
                idNode.addAttribute(TYPE_ATTRIBUTE, TYPE_ATTRIBUTE_VALUE);
                //为id节点添加列属性
                idNode.addAttribute(COLUMN, mdf.getColName());
                /**为id节点添加generator节点*/
                Element generatorNode = idNode.addElement(GENERATOR_NODE);
                /**为generator节点添加class属性*/
                generatorNode.addAttribute(GENERATOR_CLASS_ATTRIBUTE,
                        GENERATOR_CLASS_ATTRIBUTE_TYPE);
                
                break;
            }
        }
        for(MetaDataField mdf : metaDataFields){
            if(mdf.getIsPk()){
                continue;
            }else if(mdf.getFieldType()!= null && mdf.getFieldType().equalsIgnoreCase(OUT_FK)){//判断是否引用外部数据字典及数据字典类型生成多对一关联关系
                Element manyToOneNode = classNode.addElement(MANY_TO_ONE);
                manyToOneNode.addAttribute(NAME_ATTRIBUTE, mdf.getPrivateName());
                MetaDataField mdfFind = metaDataFiledDao.get(mdf.getRefValue());
                if(mdfFind ==null){
                	continue;
                }
                MetaDataTable mdt = mdfFind.getMetaDataTable();
                List<MetaDataField> list = metaDataFiledDao.getByProperty("metaDataTable.metaDataTableId", mdt.getMetaDataTableId());
                String nameAttribute = "";
                for(MetaDataField m:list){
                    if(m.getIsPk()){
                        nameAttribute = m.getPrivateName();
                        break;
                    }
                    
                }
                manyToOneNode.addAttribute(ENTITY_NAME, mdfFind.getMetaDataTable().getEntityClassName());
                //manyToOneNode.addAttribute(PROPERTY_REF, nameAttribute);
                Element column = manyToOneNode.addElement(COLUMN);
                column.addAttribute(NAME_ATTRIBUTE, mdf.getColName());
            }else if(mdf.getFieldType()!=null && mdf.getFieldType().equalsIgnoreCase(IN_FK)){//判断是否引用内部数据字典
                Element property = classNode.addElement(PROPERTY_NODE);
                property.addAttribute(NAME_ATTRIBUTE,mdf.getPrivateName());
                property.addAttribute(COLUMN,mdf.getColName());
                property.addAttribute(TYPE_ATTRIBUTE,TYPE_ATTRIBUTE_VALUE);//如果引用内部数据字典，则该列对应的类型均为long型
            }else{
                Element property = classNode.addElement(PROPERTY_NODE);
                property.addAttribute(NAME_ATTRIBUTE,mdf.getPrivateName());
                property.addAttribute(COLUMN,mdf.getColName());
                property.addAttribute(TYPE_ATTRIBUTE, mdf.getFieldType());
            }
        }
        return document;
        
    }
    
    /**
     * 创建Hibernate.cfg.xml文件
     * @param dataSourceInfo
     * @return
     */
    public  static Document createDataSourceXml(DataSourceInfo dataSourceInfo){
        Assert.notNull(dataSourceInfo,"dataSourceInfo不能为null");
        /** 建立document对象 */
        Document document = DocumentHelper.createDocument();
        /**
         * 定义文档类型
         */
        document.addDocType(DOC_DATASOURCE[0], DOC_DATASOURCE[1], DOC_DATASOURCE[2]);
        /** 建立XML文档的根节点hibernate-configuration */
        Element hibernate_configuration = document.addElement(CONFIGURATION_NODE);
        Element session_factory = hibernate_configuration.addElement(SESSION_NODE);
        Element property1 = session_factory.addElement(PROPERTY_NODE);
        
        if(dataSourceInfo.getShowSql() != null && !dataSourceInfo.getShowSql().equals("")){
            property1.addAttribute(NAME_ATTRIBUTE,SHOW_SQL);
            property1.addComment(dataSourceInfo.getShowSql());
        }else{
            property1.addAttribute(NAME_ATTRIBUTE,SHOW_SQL);
            property1.addText("true");//若指定的数据源没有show_sql则默认为true;
        }
        Element property2 = session_factory.addElement(PROPERTY_NODE);
        
        if(dataSourceInfo.getFormatSql() != null && !dataSourceInfo.getFormatSql().equals("")){
            property2.addAttribute(NAME_ATTRIBUTE,FORMAT_SQL);
            property2.addText(dataSourceInfo.getFormatSql());
        }else{
            property2.addAttribute(NAME_ATTRIBUTE,FORMAT_SQL);
            property2.addText("true");
        }
        Element property3 = session_factory.addElement(PROPERTY_NODE);
        property3.addAttribute(NAME_ATTRIBUTE,HIBERNATE_DIALECT);
        property3.addText(dataSourceInfo.getDialect());
        
        Element property4 = session_factory.addElement(PROPERTY_NODE);
        property4.addAttribute(NAME_ATTRIBUTE,HIBERNATE_CONNECTION_DRIVER_CLASS);
        property4.addText(dataSourceInfo.getDriverClass());
        
        Element property5 = session_factory.addElement(PROPERTY_NODE);
        property5.addAttribute(NAME_ATTRIBUTE,HIBERNATE_CONNECTION_URL);
        property5.addText(dataSourceInfo.getUrl());
        
        Element property6 = session_factory.addElement(PROPERTY_NODE);
        property6.addAttribute(NAME_ATTRIBUTE,HIBERNATE_CONNECTION_USERNAME);
        if(dataSourceInfo.getUsername()!=null){
            property6.addText(dataSourceInfo.getUsername());
        }
        
        Element property7 = session_factory.addElement(PROPERTY_NODE);
        property7.addAttribute(NAME_ATTRIBUTE,HIBERNATE_CONNECTION_PASSWORD);
        if(dataSourceInfo.getPassword()!=null){
            property7.addText(dataSourceInfo.getUsername());
        }
        
        Element property8 = session_factory.addElement(PROPERTY_NODE);
        property8.addAttribute(NAME_ATTRIBUTE,CURRENT_SESSION_CONTEXT_CLASS);
        property8.addText("thread");
        return document;
    }
    
}
